## MyFocusFrame.py - Does all of the GUI layout
## Copyright (C) January 26, 2005 Erick P. Bodine
## email: erick_bodine@comcast.net
## webaddress

## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 2
## of the License, or any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

# TODO: Make all single column ListCtrls wrapped wxListBoxs (Projects & Logs)
# TODO: When mouse click on ListCtrl, select first item in list
# TODO: Clear out project_text when all the projects are deleted
# TODO: Add wxFileHistory to file menu - keeps history of opened files
# TODO: The range on DatePickerCtrl(s) should be set to not accept anything
#       earlier than 1/1/2000

# BUG: When a log delete is canceled, the log is still deleted

# NOTE: Special attributes that keep state, etc.:
#   self.Backend
#   self.Properties
#   self.currentProject = ""
#   self.currentItem = 1
#   self.CurrentTab = (0, "Tasks") 
#   self.CurrentTask = ""
#   self.CurrentLog = ""
#   self.SaveState (True or False)

"""
NAME
MyFocusFrame.py

SYNOPSIS

DESCRIPTION
This module does all of the gui layout stuff

AUTHOR
Erick P Bodine
"""

import wx
import os, re, sys, time, pprint, logging
import FocusConfig, FocusDialogs, FocusEmail, FocusExceptions, FocusBackend

__author__ = "$Author: ebodine $"
__date__ = "$Date: 2005-05-15 07:40:03 -0600 (Sun, 15 May 2005) $"
__version__="$Revision: 49 $"

log = logging.getLogger("myfocus")

[FRAME_ID, NEW_ID, OPEN_ID, SAVE_ID, SAVEAS_ID, CLOSE_ID, EXIT_ID, ABOUT_ID,
EMAIL_ID, PROJECT_NEW_ID, PROJECT_DELETE_ID, PROJECT_CHANGE_ID, HELP_ID,
ACTIVITY_LOG_ID, GLOBAL_SETTINGS_ID, TASK_PANEL_ID, LOG_PANEL_ID,
COMPLETED_TASK_ID, RECENT_FILES_ID, ADD_TASK_ID, DELETE_TASK_ID, COPY_ID,
PASTE_ID, PROJECT_LIST_ID, NEW_LOG_ID, DELETE_LOG_ID, EMAIL_TEMPLATE_ID,
EMAIL_VIEW_ID, EMAIL_SEND_ID, TASK_LIST_ID, LOG_LISTBOX_ID, OUTSTANDING_ID
] = map(lambda _init_main: wx.NewId(), range(32))


def create(parent, propertiesFile):
    return MainFrame(parent, propertiesFile)
    
class MainFrame(wx.Frame):
    def _init_main(self, parent, propertiesFile):
        wx.Frame.__init__(self, parent, FRAME_ID, "", wx.DefaultPosition)
        
        # Get the wxfocus properties first
        self.Properties = FocusConfig.PropertiesParser(propertiesFile)

        self.icon = wx.Icon(os.path.normpath('images\\srnc.ico'), wx.BITMAP_TYPE_ICON)
        self.SetIcon(self.icon)

        #--- Menu Bar ---#
        self.wxfocus_frame_menubar = wx.MenuBar()
        self.SetMenuBar(self.wxfocus_frame_menubar)

        self.MenuFile = wx.Menu()
        self.MenuFile.Append(NEW_ID, "New\tCtrl+N", "", wx.ITEM_NORMAL)
        self.MenuFile.Append(OPEN_ID, "Open\tCtrl+O", "", wx.ITEM_NORMAL)

        self.submenu = wx.Menu()
        TEMP_ID = wx.NewId()
        self.submenu.Append(TEMP_ID, "recent file")
        self.MenuFile.AppendMenu(RECENT_FILES_ID, "Open Recent", self.submenu)

        self.MenuFile.AppendSeparator()
        self.MenuFile.Append(SAVE_ID, "Save\tCtrl+S", "", wx.ITEM_NORMAL)
        self.MenuFile.Append(SAVEAS_ID, "SaveAs", "", wx.ITEM_NORMAL)
        self.MenuFile.AppendSeparator()
        self.MenuFile.Append(CLOSE_ID, "Close\tCtrl+W", "", wx.ITEM_NORMAL)
        self.MenuFile.AppendSeparator()
        self.MenuFile.Append(EXIT_ID, "Exit\tCtrl+Q", "", wx.ITEM_NORMAL)
        self.wxfocus_frame_menubar.Append(self.MenuFile, "File")

        self.MenuEdit = wx.Menu()
        self.MenuEdit.Append(COPY_ID, "Copy", "", wx.ITEM_NORMAL)
        self.MenuEdit.Append(PASTE_ID, "Paste", "", wx.ITEM_NORMAL)
        self.wxfocus_frame_menubar.Append(self.MenuEdit, "Edit")
        
        ## Report Status
        self.MenuReportStatus = wx.Menu()
        self.submenuEmail = wx.Menu()
        self.submenuEmail.Append(EMAIL_TEMPLATE_ID, "Edit Email Template", "", wx.ITEM_NORMAL)
        self.submenuEmail.Append(EMAIL_VIEW_ID, "Preview Email", "", wx.ITEM_NORMAL)
        self.submenuEmail.Append(EMAIL_SEND_ID, "Send Email", "", wx.ITEM_NORMAL)
        
        self.submenuFtp = wx.Menu()
        self.submenuFtp.Append(-1, "ftp")
        
        self.MenuReportStatus.AppendMenu(-1, "Email", self.submenuEmail)
        self.MenuReportStatus.AppendMenu(-1, "FTP", self.submenuFtp)
        self.MenuReportStatus.Append(OUTSTANDING_ID, "Outstanding Tasks", "", wx.ITEM_NORMAL)
        self.wxfocus_frame_menubar.Append(self.MenuReportStatus, "Status")
        
        ## Settings
        self.MenuUtilities = wx.Menu()
        self.MenuUtilities.Append(ACTIVITY_LOG_ID, "View Activity Log", "", wx.ITEM_NORMAL)
        self.MenuUtilities.AppendSeparator()
        self.MenuUtilities.Append(GLOBAL_SETTINGS_ID, "Global Options", "", wx.ITEM_NORMAL)
        self.wxfocus_frame_menubar.Append(self.MenuUtilities, "Utilities")
        
        ## Help/About
        self.MenuHelp = wx.Menu()
        self.MenuHelp.Append(HELP_ID, "Help", "", wx.ITEM_NORMAL)
        self.MenuHelp.AppendSeparator()
        self.MenuHelp.Append(ABOUT_ID, "About", "", wx.ITEM_NORMAL)
        self.wxfocus_frame_menubar.Append(self.MenuHelp, "Help")

        # Set the menu accellerator table...
        table = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), EXIT_ID),
                                        (wx.ACCEL_CTRL, ord('N'), NEW_ID),
                                        (wx.ACCEL_CTRL, ord('W'), CLOSE_ID),
                                        (wx.ACCEL_CTRL, ord('S'), SAVE_ID),
                                        (wx.ACCEL_CTRL, ord('O'), OPEN_ID)])
        self.SetAcceleratorTable(table)

        # Menu Bar Event Handlers
        self.Bind(wx.EVT_MENU, self.OnExit, id=EXIT_ID)
        self.Bind(wx.EVT_MENU, self.OnSave, id=SAVE_ID)
        self.Bind(wx.EVT_MENU, self.OnSaveAs, id=SAVEAS_ID)
        self.Bind(wx.EVT_MENU, self.OnClose, id=CLOSE_ID)
        self.Bind(wx.EVT_MENU, self.OnOpen, id=OPEN_ID)
        self.Bind(wx.EVT_MENU, self.OnNew, id=NEW_ID)
        self.Bind(wx.EVT_MENU, self.OnGlobalSettings, id=GLOBAL_SETTINGS_ID)
        self.Bind(wx.EVT_MENU, self.OnActivityLog, id=ACTIVITY_LOG_ID)
        self.Bind(wx.EVT_MENU, self.OnHelp, id=HELP_ID)
        self.Bind(wx.EVT_MENU, self.OnAbout, id=ABOUT_ID)
        self.Bind(wx.EVT_MENU, self.OnSendEmail, id=EMAIL_SEND_ID)
        self.Bind(wx.EVT_MENU, self.OnEditEmail, id=EMAIL_TEMPLATE_ID)
        self.Bind(wx.EVT_MENU, self.OnPreviewEmail, id=EMAIL_VIEW_ID)
        self.Bind(wx.EVT_MENU, self.OnOutstandingTasks, id=OUTSTANDING_ID)

        #--- Status Bar ---#
        self.wxfocus_frame_statusbar = self.CreateStatusBar(2)
        self.wxfocus_frame_statusbar.SetStatusWidths([-1, 150])
        self.SetStatusText("date here", 1)
        self.SetStatusText("Welcome to MyFocus")

        #--- Tool Bar ---#
        # NOTE: Changed to using the toolbar property
        if self.Properties.get('TOOLBAR.VIEW') == 'True':
            self.toolbar = wx.ToolBar(self, -1,
                                            style=wx.TB_HORIZONTAL|wx.NO_BORDER|wx.TB_FLAT|wx.TB_TEXT)
    
            self.toolbar.AddSimpleTool(10,
                                    wx.Image('images\\new.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap(),
                                    "New", "Long help for New")
    
            self.toolbar.AddSimpleTool(10,
                                    wx.Image('images\\open.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap(),
                                    "Open", "Long help for Open")
    
            self.toolbar.AddSimpleTool(10,
                                    wx.Image('images\\save.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap(), 
                                    "Save", "Long help for Save")
    
            self.toolbar.AddSeparator()
    
            self.toolbar.AddSimpleTool(10, 
                            wx.Image('images\\copy.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap(), "Copy")
    
            self.toolbar.AddSimpleTool(10,
                                wx.Image('images\\paste.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap(),
                                "Paste")
            self.SetToolBar(self.toolbar)

        # Tool Bar Event Handlers


        #--- Create Controls ---#

        self.notebook = FocusNotebook(self)

        self.projects_label = wx.StaticText(self, -1, "Project")
        self.projects_listctrl = wx.ListBox(self, PROJECT_LIST_ID, wx.DefaultPosition, wx.DefaultSize,
                                        [], wx.LB_SINGLE|wx.LB_NEEDED_SB, wx.DefaultValidator)
        self.new_project_button = wx.Button(self, PROJECT_NEW_ID, "New")
        self.delete_project_button = wx.Button(self, PROJECT_DELETE_ID, "Delete")
        self.vertical_static_line_1 = wx.StaticLine(self, -1, style=wx.LI_VERTICAL)
        self.vertical_static_line_2 = wx.StaticLine(self, -1, style=wx.LI_VERTICAL)
        self.name_label = wx.StaticText(self, -1, "Name")
        self.project_text = wx.TextCtrl(self, -1, "")
        self.change_project_button = wx.Button(self, PROJECT_CHANGE_ID, "Change Project Name")
        self.horizontal_static_line = wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL)
        
        #----------------------#
        # Frame Event Handlers #
        #----------------------#
        self.Bind(wx.EVT_BUTTON, self.NewProject, id=PROJECT_NEW_ID)
        self.Bind(wx.EVT_BUTTON, self.DeleteProject, id=PROJECT_DELETE_ID)
        self.Bind(wx.EVT_BUTTON, self.ChangeProject, id=PROJECT_CHANGE_ID)
        self.Bind(wx.EVT_LISTBOX, self.OnProjectSelected, id=PROJECT_LIST_ID)

        #-------------------------#
        # Notebook Event Handlers #
        #-------------------------#
        # TODO: Remove this event handler + event method
#        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
        
        self.Bind(wx.EVT_BUTTON, self.CompleteTask, id=COMPLETED_TASK_ID)
        self.Bind(wx.EVT_BUTTON, self.AddTask, id=ADD_TASK_ID)
        self.Bind(wx.EVT_BUTTON, self.DeleteTask, id=DELETE_TASK_ID)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnTaskListSelected, id=TASK_LIST_ID)
        self.Bind(wx.EVT_LISTBOX, self.OnLogListSelected, id=LOG_LISTBOX_ID)

        self.Bind(wx.EVT_BUTTON, self.NewLog, id=NEW_LOG_ID)
        self.Bind(wx.EVT_BUTTON, self.DeleteLog, id=DELETE_LOG_ID)


        self.__set_initial_properties()
        self.__do_layout()
        
    def __create_menu(self):
        pass

    def __set_initial_properties(self):
        """ Set initial control properties. """
        self.SetTitle("MyFocus")
        self.SaveState = False
        
        # NOTE: Causing C++ assertion on Linux (?)
        # NOTE: Added system platform test to enable toolbar on Win32
        # NOTE: Changed to using the toolbar property
        if self.Properties.get('TOOLBAR.VIEW') == 'True':
            self.toolbar.Realize()
        
        self.CurrentBackendFile = self.Properties.get('BACKEND.FILE')
        self.Backend = FocusBackend.getBackend(self.Properties.get('BACKEND.TYPE'),
                                                self.Properties.get('BACKEND.MODULE'),
                                                self.Properties.get('BACKEND.CLASS'),
                                                self.CurrentBackendFile)
                                                
        # Get all of the projects and populate the projects list ctrl if there are any
        projects = self.Backend.getProjects()
        if projects:
            self.currentProject = projects[0]
        else:
            self.currentProject = ""
        
        self.current_item = 0
        
        for project in projects:
            log.debug("Found project: %s" % project)
            self.projects_listctrl.Append(project)

        self.projects_listctrl.SetSelection(0)

        # Populate the project text control
        self.project_text.Clear()
        self.project_text.WriteText(self.currentProject)
        
        # Set the CurrentTab attribute
        # If the notebooPage is other than the first page (0), the SetSelection() method
        #  triggers the EVT_NOTEBOOK_PAGE_CHANGED event and the proper __populate*() method
        #  will be called as well.
        notebookPage = int(self.Properties.get('NOTEBOOK.STARTING_TAB'))
        if notebookPage != 0:
            self.notebook.SetSelection(notebookPage)
        else:
            sel = self.notebook.GetSelection()
            tabName = self.notebook.GetPageText(sel)
            self.CurrentTab = (sel, tabName)
            log.debug("inital tab selection: %d, tab name: %s" % (sel, tabName))
            
            # Populate the 'task' tab
            self.__populateTasks()

    def __populateTasks(self):
        """ Populate the 'Tasks' tab in the notebook. """
        log.debug("In __populateTasks ...")
        tasksData = self.Backend.getProjectTasks(self.currentProject)

        self.notebook.task_list_ctrl.DeleteAllItems()   # Clear out the notebook tasks list
        
        # Get the current task
        if tasksData:
            self.CurrentTask = tasksData[0]
        else:
            self.CurrentTask = ""
            
        if self.CurrentTask:
            ## Populate the notebook 'task' list control
            for i,task in enumerate(tasksData):
                taskInfo = self.Backend.getTaskInfo(self.currentProject, task)
                self.notebook.task_list_ctrl.InsertStringItem(0, str(i))
                self.notebook.task_list_ctrl.SetStringItem(0, 1, taskInfo[3])
                self.notebook.task_list_ctrl.SetStringItem(0, 2, taskInfo[0])
                
            # Title
            self.notebook.title_text_ctrl.Clear()
            self.notebook.title_text_ctrl.WriteText(self.CurrentTask)
            
            # Comments
            self.notebook.comments_text_ctrl.Clear()
            taskInfo = self.Backend.getTaskInfo(self.currentProject, self.CurrentTask)
            self.notebook.comments_text_ctrl.WriteText(taskInfo[2])
            
            # Start Date
            sm,sd,sy = self.__splitDateTime(taskInfo[4].split(' ')[0])
            self.notebook.startDatePicker.SetValue(wx.DateTimeFromDMY(day=sd,
                            month=sm,year=sy,hour=0,minute=0,second=0,millisec=0))
            
            # End Date
            em,ed,ey = self.__splitDateTime(taskInfo[5].split(' ')[0])
            self.notebook.endDatePicker.SetValue(wx.DateTimeFromDMY(day=ed,
                            month=em,year=ey,hour=0,minute=0,second=0,millisec=0))
        else:
            self.notebook.title_text_ctrl.Clear()
            self.notebook.comments_text_ctrl.Clear()
                            
    def __populateLogs(self):
        """ Populate the 'Logs' tab in the notebook. 
        Relevant controls:
            self.notebook.log_list_ctrl
            self.notebook.date_text_ctrl
            self.notebook.body_text_ctrl
        """
        log.debug("In __populateLogs ...")
        logsData = self.Backend.getProjectLogs(self.currentProject)
        
        self.notebook.log_list_ctrl.Set([])     # Clear out the notebook logs list
        
        if logsData:
            self.CurrentLog = logsData[len(logsData)-1]
        else:
            self.CurrentLog = ""
            log.debug("No log data for project (%s). Current log empty" % self.currentProject)
            
        if self.CurrentLog:
            # Populate the logs list control
            for mflog in logsData[::-1]:
                self.notebook.log_list_ctrl.Append(mflog)
            self.notebook.log_list_ctrl.SetSelection(0)
            
            # Clear out and populate the date text control
            self.notebook.date_text_ctrl.Clear()
            self.notebook.date_text_ctrl.WriteText(self.CurrentLog)
            
            # Clear out and populate the body text control
            loginfo = self.Backend.getLogInfo(self.currentProject, self.CurrentLog)
            self.notebook.body_text_ctrl.Clear()
            self.notebook.body_text_ctrl.WriteText(loginfo[1])
            
        else:
            self.notebook.date_text_ctrl.Clear()
            self.notebook.body_text_ctrl.Clear()
            
    def __populateOverview(self):
        """ Populate the 'Overview' tab in the notebook
        Relevant control(s):
            self.notebook.task_overview_list_ctrl
        """
        log.debug("In __populateOverview ...")
        
        self.notebook.task_overview_list_ctrl.DeleteAllItems()  # Clear out the list ctrl
        
        projects = self.Backend.getProjects()
        
        # Get all of the tasks for every project
        overview = {}
        for p in projects:
            overview[p] = self.Backend.getProjectTasks(p)
            
        if overview:
            index=0
            for project,tasks in overview.iteritems():
                for item in tasks:
                    self.notebook.task_overview_list_ctrl.InsertStringItem(0, str(index))
                    self.notebook.task_overview_list_ctrl.SetStringItem(0, 1, "0")
                    self.notebook.task_overview_list_ctrl.SetStringItem(0, 2, project)
                    self.notebook.task_overview_list_ctrl.SetStringItem(0, 3, item)
                    index += 1
                    
    def __splitDateTime(self, dateString):
        """ Split the date string and return the constituent parts as ints
        Params:
            dateString = The date string ('12/23/2005')
        """
        m,d,y = [ int(x) for x in dateString.split('/') ]
        m -= 1; y += 2000
        log.debug("__splitDateTime: %s" % dateString)
        log.debug("Split m: %d, d: %d, y: %d" % (m,d,y))
        
        return m, d, y

    def __do_layout(self):
        """ Layout the GUI elements """
        sizer_root = wx.BoxSizer(wx.HORIZONTAL)     # The root sizer

        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add(self.projects_label, 0, wx.ALIGN_LEFT|wx.TOP|wx.LEFT, 10)
        sizer_1.Add(self.projects_listctrl, 24, wx.ALL|wx.GROW, 10)

        box = wx.BoxSizer(wx.HORIZONTAL)
        box.Add(self.new_project_button, 0, wx.ALIGN_BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 2)
        box.Add(self.delete_project_button, 0, wx.ALIGN_BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 2)
        sizer_1.Add(box, 2, wx.ALL|wx.ALIGN_BOTTOM, 5)
        sizer_root.Add(sizer_1, 0, wx.GROW, 0)

        sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_2.Add(self.vertical_static_line_1, 0, 
                        wx.TOP|wx.BOTTOM|wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 5)
        sizer_2.Add(self.vertical_static_line_2, 0, 
                        wx.TOP|wx.BOTTOM|wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 5)
        sizer_root.Add(sizer_2, 0, wx.GROW, 0)

        last_sizer = wx.BoxSizer(wx.VERTICAL)
        box2 = wx.BoxSizer(wx.HORIZONTAL)
        box2.Add(self.name_label, 0, wx.TOP|wx.LEFT, 15)
        box2.Add(self.project_text, 3, wx.ALL, 10)
        box2.Add(self.change_project_button, 2, wx.ALL, 10)
        last_sizer.Add(box2, 0, wx.ALIGN_LEFT|wx.EXPAND, 0)
        last_sizer.Add(self.horizontal_static_line, 0, wx.LEFT|wx.RIGHT|wx.GROW|wx.EXPAND, 10)
        last_sizer.Add(self.notebook, 18, wx.ALL|wx.GROW|wx.EXPAND, 10)
        sizer_root.Add(last_sizer, 1, wx.ALIGN_LEFT|wx.GROW|wx.EXPAND, 0)

        self.SetSizer(sizer_root)

        self.SetSizeHints(minW=925, minH=530)
        self.SetSize(wx.Size(925,530))

    def __init__(self, parent, propertiesFile):
        self._init_main(parent, propertiesFile)

    ###############################
    #    Event Handler Methods    #
    ###############################

    #--- File Menu ---#
    def OnSave(self, event):
        """ Save the currently opened backend file. """
        log.info("In OnSave ...")
        log.debug("The current tab: %d, %s" % (self.CurrentTab[0], self.CurrentTab[1]))
        
        if self.SaveState:
            log.debug("Saving data")

    def OnSaveAs(self, event):
        """ Save the currently opened backend file under a different name. """
        log.info("In OnSaveAs ...")
        
        present_file = self.Backend.databaseFile
        sad = wx.FileDialog(self, "Save file as...", os.getcwd(), present_file, "All files (*.*)|*.*", 
                                wx.SAVE|wx.HIDE_READONLY|wx.OVERWRITE_PROMPT)
        
        if sad.ShowModal() == wx.ID_OK:
            new_file = sad.GetPath()
            
            log.debug("New file: [ %s ]" % new_file)
            log.debug("Old file: [%s ]" % present_file)
            
            try:
                os.rename(present_file, new_file)
                self.Properties.saveNewProperty('backend.file', new_file)
                
            except (IOError,OSError), error:
                log.critical("Unable to rename \"%s\" to \"%s\" : (%s)" % \
                                (present_file, new_file, str(error)))

    def OnClose(self, event):
        """ Close the currently opened backend file. """
        log.info("In OnClose ...")
        log.debug("The current tab: %d, %s" % (self.CurrentTab[0], self.CurrentTab[1]))
        
    def OnOpen(self, event):
        """ Open a new backend file. """
        log.info("In Open ...")
        log.debug("The current tab: %d, %s" % (self.CurrentTab[0], self.CurrentTab[1]))
        
        ofd = wx.FileDialog(self, "Open data file", os.getcwd(), "", "All files (*.*)|*.*", 
                                wx.OPEN)
        if ofd.ShowModal():
            new_file = ofd.GetPath()
            log.debug("Opening %s" % new_file)
            
            # TODO: Check that we can actually use the file
            # TODO: Check to see what type of backend file (XML, SQLite, metakit)
            
            self.Backend.databaseFile = new_file
            self.currentProject = self.Backend.getCurrentProject()
            
            if self.CurrentTab[0] == 0:
                self.notebook.task_list_ctrl.DeleteAllItems()   # Clear out the 'Tasks' tab
                self.__populateTasks()
                
            elif self.CurrentTab[0] == 1:
                self.notebook.log_list_ctrl.Set([])     # Clear out the notebook logs list
                self.__populateLogs()
                
            else:
                try:
                    log.error("Don't know what the current tab is: %d, %s" % \
                                (self.CurrentTab[0], self.CurrentTab[1]))
                except IndexError:
                    log.error("something wrong with self.CurrentTab!!")
                    print self.CurrentTab
            
    def OnExit(self, event):
        """ Exit the application. """
        log.info("Exiting MyFocus...")
        log.debug("SaveState: %s" % str(self.SaveState))
        log.debug("The current tab: %d, %s" % (self.CurrentTab[0], self.CurrentTab[1]))
        
        if self.SaveState:
            log.debug("Saving data")
        
        self.Close()

    def OnNew(self, event):
        """ Start a new backend file. """
        log.debug("In OnNew...")
        log.debug("The current tab: %d, %s" % (self.CurrentTab[0], self.CurrentTab[1]))
        
        if self.CurrentTab[0] == 0:
            self.notebook.task_list_ctrl.DeleteAllItems()   # Clear out the 'Tasks' tab
        elif self.CurrentTab[0] == 1:
            self.notebook.log_list_ctrl.Set([])     # Clear out the notebook logs list
        else:
            try:
                log.error("Don't know what the current tab is: %d, %s" % \
                            (self.CurrentTab[0], self.CurrentTab[1]))
            except IndexError:
                log.error("something wrong with self.CurrentTab!!")
                print self.CurrentTab
                
        self.SaveState = True
        self.Backend.databaseFile = self.Properties.get('BACKEND.NEWFILE')


    #--- Status menu ---#
    def OnPreviewEmail(self, event):
        """ Preview the email. """
        log.debug("In OnPreviewEmail...")
        preview = FocusEmail.PreviewEmailDialog(self, self.Properties)
        preview.Destroy()
        
    def OnEditEmail(self, event):
        """ Edit the email template. """
        log.debug("In OnEditEmail...")
        edit = FocusEmail.EditTemplateDialog(self, self.Properties.get('EMAIL.TEMPLATE'))
        edit.Destroy()
        
    def OnSendEmail(self, event):
        """ Send email. """
        log.debug("In OnSendEmail...")
        send = FocusEmail.SendEmailDialog(self)
        send.Destroy()

    def OnOutstandingTasks(self, event):
        """ See all tasks. """
        log.debug("In OnOutstandingTasks ...")

    #--- Utilities Menu ---#
    def OnGlobalSettings(self, event):
        """ View/Change any global settings. """
        print "In OnGlobalSettings..."

    def OnActivityLog(self, event):
        """ View the Activity/Debug log file. """
        log.info("In OnActivityLog ...")


    #--- Help Menu ---#
    def OnHelp(self, event):
        """ Show the help. """
        log.debug("In OnHelp ...")

    def OnAbout(self, event):
        """ Show the About. """
        about = FocusDialogs.AboutDialog(self)

    #--- Frame Buttons ---#
    def NewProject(self, event):
        """ Create a new project. """
        log.debug("In NewProject")

        title = "New Project - MyFocus"
        newProjectDialog = FocusDialogs.NewProjectDialog(self, title, "Enter a project name: ")
        
        if newProjectDialog.value == wx.ID_OK:
            newProjectName = newProjectDialog.text.GetLineText(0)
            
            # Add the project to the database
            try:
                self.Backend.createProject(newProjectName)
                
            except (FocusExceptions.DuplicateTableName,FocusExceptions.DatabaseError), error:
                log.error("%s" % error.value)
                
            else:
                self.projects_listctrl.Append(newProjectName)
                self.currentProject = newProjectName
                
                self.project_text.Clear()
                self.project_text.WriteText(self.currentProject)
            
    def DeleteProject(self, event):
        """ Delete the currently selected project. """
        log.info("In DeleteProject")

        if not self.currentProject:
            np = wx.MessageDialog(self, "You need to select a project!", "Select a project",
                                     wx.OK|wx.ICON_INFORMATION)
            np.ShowModal()
            np.Destroy()
            return None
            
        log.debug("Project Selected for deletion: %s" % self.currentProject)
        
        # Build the delete project dialog
        mode = "PROJECT"
        caption = "Delete the selected Project?"
        title = "Confirm Project Delete - wxfocus"
        dd = FocusDialogs.GenericDeleteDialog(self, caption, title, mode)
        
        if dd.ShowModal() == wx.ID_CANCEL:
            dd.Destroy(); return None
            
        # Delete the project from the database and clear out the item from the
        #  project list control
        self.Backend.deleteProject(self.currentProject)
        
        self.projects_listctrl.Set([])
        projects = self.Backend.getProjects()
        for p in projects.sort():
            self.projects_listctrl.Append(p)
            
        self.projects_listctrl.SetSelection(0)
        self.currentProject = self.projects_listctrl.GetStringSelection()
        log.debug("current project: %s" % self.currentProject)

        self.project_text.Clear()
        self.project_text.WriteText(self.currentProject)

    def ChangeProject(self, event):
        """ Event handler for changing the project name. """
        log.info("In ChangeProject")

        current_name = self.project_text.GetLineText(0)
        log.debug("Changing project: %s" % current_name)

        title = "Change Project Name - MyFocus"
        cpd = FocusDialogs.ChangeProjectDialog(self, title, "Enter a project name: ", current_name)

        if cpd.value == wx.ID_OK:
            new_name = cpd.text.GetLineText(0)
            if new_name != current_name and new_name != "":
                log.debug("New project name: %s" % new_name)
                
                self.Backend.renameProject(current_name, new_name)
                
                # Re-populate the 'Projects' ListBox
                new_index = 0
                self.projects_listctrl.Set([])
                projects = self.Backend.getProjects()
                for i,p in enumerate(projects):
                    if re.search(new_name, p): 
                        new_index = i
                    self.projects_listctrl.Append(p)
                    
                self.projects_listctrl.SetSelection(new_index)
                self.currentProject = self.projects_listctrl.GetStringSelection()  # reset the currentProject
                log.debug("Current project: %s" % self.currentProject)
                
                # Populate the notebook tab
                currentTab = self.CurrentTab[0]
                if currentTab == 0:
                    self.__populateTasks()
                elif currentTab == 1:
                    self.__populateLogs()
                
                # Populate 
                self.project_text.Clear()
                self.project_text.WriteText(new_name)
                
        else:
            # TODO: Need consistent calling/handling/closing of dialogs
            cpd.Destroy()

        cpd.Destroy()

    #--- Project List ---#
    def OnProjectSelected(self, event):
        """ Event Handler for selecting individual projects in the projects list control.
        This method populates the special 'self.currentProject' attribute
        """
        log.info("In OnProjectSelected")
        
        self.currentProject = event.GetString()
        log.debug("Selected project: %s" % self.currentProject)
        
        # Populate the project text box
        self.project_text.Clear()
        self.project_text.WriteText(self.currentProject)
        
        # Populate the various Notebook tab controls
        currentTab = self.CurrentTab[0]
        log.debug("current notebook tab: %d" % currentTab)
        
        if currentTab == 0:
            self.__populateTasks()
            
        elif currentTab == 1:
            self.__populateLogs()
            
        #elif currentTab == 2:
        #    self.__populateOverview()
        
        else:
            log.error("I don't know what notebook tab this is")        

    #--------------------------------#
    # Notebook Event Handler Methods #
    #--------------------------------#
    
    def OnPageChanging(self, event):
        """ The notebook tab is changing. """
        log.debug("Notebook page changing")
        
        old = event.GetOldSelection()
        new = event.GetSelection()
        sel = self.notebook.GetSelection()
        
        # NOTE: GetOldSelection() returns the same number as GetSelection() on Windows!
        log.debug("Old tab: %d, New tab: %d, Selection: %d" % (old, new, sel))
        
        # TODO: Commit the db whenever a notebook tab changes
        # NOTE: We can tell if a body or comment text control has changed by catching the EVT_TEXT
        #  event.  Changing a self.TextEdit variable to 1 should be able to track this.
        #
        #  Thus OnPageChanging() or On<ListCtrl>Selected() we can check self.TextEdit and save to the
        #  database if necessary.  This will necessitate keeping track of the notebook tab as well
        #  with self.NotebookTab = 0,1 or 2
        #
        #  The OnSave method should also be able to save the edits and set self.TextEdit == 0
        #
        #  Need to investigate if the __populate*() methods might trigger EVT_TEXT by calling the
        #  WriteText() method.  If so, the self.TextEdit variable will have to be reset to 0
        
#        if new == 0:
#            self.__populateTasks()
#        elif new == 1:
#            self.__populateLogs()
#        elif new == 2:
#            self.__populateOverview()
#        else:
#            log.error("Invalid notebook tab!")
            
    def OnPageChanged(self, event):
        log.debug("Notebook page changed")
        
        sel = event.GetSelection()
        tabName = self.notebook.GetPageText(sel)
        self.CurrentTab = (sel, tabName)
        
        log.debug("selection: %d, Tab: %s" % (sel, tabName))
        
        if sel == 0:
            self.__populateTasks()
        elif sel == 1:
            self.__populateLogs()
            pass
        #elif sel == 2:
        #    self.__populateOverview()
        else:
            log.error("I don't know this notebook tab!")

    def CompleteTask(self, event):
        """ Complete the task. """
        log.debug("In CompleteTask")
        log.debug("Current Project: %s, Current Task: %s" % (self.currentProject, self.CurrentTask))
        
        ctd = FocusDialogs.CompleteTaskDialog(self, self.notebook.comments_text_ctrl.GetValue())
        
        if ctd.value == wx.ID_OK:
            log.debug("Completing task ...")
            
            log_check_value = ctd.log_check.GetValue()
            schedule_check_value = ctd.schedule_check.GetValue()
            
            # Log the completed task
            if log_check_value:
                log.debug("Logging the completed task")
                
                logDate = ctd.changeDatePicker.GetValue().Format("%m/%d/%Y")
                logDate += " "+time.strftime("%H:%M:%S")
                
                logBody = ctd.body_text.GetValue()
                
                self.Backend.addLog(self.currentProject, logDate, logBody)
                
            # Schedule a follow up task
            if schedule_check_value:
                log.debug("Scheduling a follow up task")
                
                taskTitle = ctd.title_text.GetValue()
                taskComments = ctd.comments_text.GetValue()
                startDate = ctd.startDatePicker.GetValue().FormatDate()
                endDate = ctd.endDatePicker.GetValue().FormatDate()
                
                self.Backend.addTask(self.currentProject, taskTitle, taskComments, startDate, endDate)
                
            # Clean up
            if not log_check_value and not schedule_check_value:
                log.debug("Neither check box checked!")
                
                message = "Not logging or scheduling follow-up task\nOK will delete the task forever!!"
                dlg = wx.MessageDialog(self, message, "Remove Task?", 
                                            wx.OK|wx.CANCEL|wx.ICON_EXCLAMATION)
                value = dlg.ShowModal()
                if value == wx.ID_OK:
                    log.debug("Removing task: %s" % self.CurrentTask)
                    self.Backend.deleteTask(self.currentProject, self.CurrentTask)
                    self.__populateTasks()
                dlg.Destroy()
                
            else:
                self.Backend.deleteTask(self.currentProject, self.CurrentTask)
                self.__populateTasks()
        
    def AddTask(self, event):
        """ Add a task to the currently selected project. """
        log.debug("in AddTask")
        
        at = FocusDialogs.AddTaskDialog(self)
        
        if at.value == wx.ID_OK:
            newTaskName = at.titleTextCtrl.GetLineText(0)
            startDate = at.startDatePicker.GetValue().Format("%m/%d/%Y")
            endDate = at.endDatePicker.GetValue().Format("%m/%d/%Y")
            comments = at.commentsTextCtrl.GetValue()
            
            log.debug("New task name: %s" % newTaskName)
            log.debug("startDate: %s" % startDate)
            log.debug("endDate: %s" % endDate)
            
            self.Backend.addTask(self.currentProject, newTaskName, comments, startDate, endDate)
            
            self.notebook.title_text_ctrl.Clear()
            self.notebook.title_text_ctrl.WriteText(newTaskName)
            
            self.notebook.comments_text_ctrl.Clear()
            self.notebook.comments_text_ctrl.WriteText(comments)
            
            self.notebook.task_list_ctrl.InsertStringItem(0, str(0))
            self.notebook.task_list_ctrl.SetStringItem(0, 1, '0')
            self.notebook.task_list_ctrl.SetStringItem(0, 2, newTaskName)
            
            # Start Date
            sm,sd,sy = self.__splitDateTime(startDate)
            self.notebook.startDatePicker.SetValue(wx.DateTimeFromDMY(day=sd, month=sm,year=sy,hour=0,
                                                                        minute=0,second=0,millisec=0))
            # End Date
            em,ed,ey = self.__splitDateTime(endDate)
            self.notebook.endDatePicker.SetValue(wx.DateTimeFromDMY(day=ed, month=em,year=ey,hour=0,
                                                                        minute=0,second=0,millisec=0))
            self.CurrentTask = newTaskName
            
    def DeleteTask(self, event):
        """ Delete the selected task from the currently selected project. """
        log.info("In DeleteTask ...")
        
        dt = FocusDialogs.GenericDeleteDialog(self, "Delete the selected task?",
                                                        "Delete a task - myfocus", "")
        if dt.ShowModal() == wx.ID_CANCEL:
            dt.Destroy()
            
        self.Backend.deleteTask(self.currentProject, self.CurrentTask)
        self.__populateTasks()
            
    def NewLog(self, event):
        """ Create a new log in the currently selected project. """
        log.info("In NewLog ...")
        nl = FocusDialogs.AddLogDialog(self)
        
        if nl.value == wx.ID_OK:
            
            pickerDate = nl.logDatePicker.GetValue().Format("%m/%d/%Y")
            newLogDate = pickerDate+" "+time.strftime('%H:%M:%S')
            log.debug("newLogDate: %s" % newLogDate)
            
            newLogBody = nl.bodyTextCtrl.GetValue()
            
            self.Backend.addLog(self.currentProject, newLogDate, newLogBody)
            
            self.notebook.log_list_ctrl.InsertItems([newLogDate], pos=0)
            self.notebook.log_list_ctrl.SetSelection(0)
        
            self.notebook.date_text_ctrl.Clear()
            self.notebook.date_text_ctrl.WriteText(newLogDate)
            
            self.notebook.body_text_ctrl.Clear()
            self.notebook.body_text_ctrl.WriteText(newLogBody)
            
            self.CurrentLog = newLogDate
            log.debug("CurrentLog: %s" % newLogDate)

    def DeleteLog(self, event):
        """ Delete selected log from the currently selected project. """
        log.info("In DeleteLog ...")
        dl = FocusDialogs.GenericDeleteDialog(self, "Delete the selected log?", 
                                                        "Delete a log - myfocus", "")
        if dl.ShowModal() == wx.ID_CANCEL:
            dl.Destroy()
            
        self.Backend.deleteLog(self.currentProject, self.CurrentLog)
        self.__populateLogs()

    def OnTaskListSelected(self, event):
        """ Event Handler for selecting a task. """
        log.info("In OnTaskListSelected ...")
        
        index = event.m_itemIndex
        self.CurrentTask = self.notebook.task_list_ctrl.GetColumnText(index, 2)
        log.debug("selected task: %s " % self.CurrentTask)
        
        taskInfo = self.Backend.getTaskInfo(self.currentProject, self.CurrentTask)
        
        # title
        self.notebook.title_text_ctrl.Clear()
        self.notebook.title_text_ctrl.WriteText(self.CurrentTask)

        # comments
        self.notebook.comments_text_ctrl.Clear()
        self.notebook.comments_text_ctrl.WriteText(taskInfo[2])
        
        # start date
        sm,sd,sy = self.__splitDateTime(taskInfo[4].split(' ')[0])
        self.notebook.startDatePicker.SetValue(wx.DateTimeFromDMY(day=sd,
                        month=sm,year=sy,hour=0,minute=0,second=0,millisec=0))
        
        # end date
        em,ed,ey = self.__splitDateTime(taskInfo[5].split(' ')[0])
        self.notebook.endDatePicker.SetValue(wx.DateTimeFromDMY(day=ed,
                        month=em,year=ey,hour=0,minute=0,second=0,millisec=0))
        
    def OnLogListSelected(self, event):
        """ Event Handler for selecting a log. """
        log.info("In OnLogListSelected ...")
        
        self.CurrentLog = event.GetString()
        log.debug("selected log: %s" % self.CurrentLog)
        
        logInfo = self.Backend.getLogInfo(self.currentProject, self.CurrentLog)
        
        # Populate date text control
        self.notebook.date_text_ctrl.Clear()
        self.notebook.date_text_ctrl.WriteText(self.CurrentLog)
        
        # Populate body text control
        self.notebook.body_text_ctrl.Clear()
        self.notebook.body_text_ctrl.WriteText(logInfo[1])


class FocusListBox(wx.ListBox):
    """ Our sub-class of the wx.ListBox control. """
    def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0,
                    validator=wx.DefaultValidator):
        wx.ListBox.__init__(self, parent, ID, pos, size, style, validator)
        
        
class FocusListCtrl(wx.ListCtrl):
    """ Our sub-class of the wx.ListCtrl. """
    def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize,
                    style=0):
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)

    def GetColumnText(self, index, column):
        """ Get the text contained in a specified column for specified
        row.
        Params:
            index =
            column = 
        """
        item = self.GetItem(index, column)
        return item.GetText()
        

class FocusNotebook(wx.Notebook):
    """ Our sub-class of wx.Notebook ."""
    def __init__(self, parent):
        wx.Notebook.__init__(self, parent, -1, style=0)

        #--- Task Panel Controls ---#
        self.task_panel = wx.Panel(self, TASK_PANEL_ID)
        self.AddPage(self.task_panel, "Tasks")

        self.task_label = wx.StaticText(self.task_panel, -1, "Task")
        self.task_list_ctrl = FocusListCtrl(self.task_panel, TASK_LIST_ID, 
                                        style=wx.SUNKEN_BORDER|wx.LC_REPORT)
        self.task_list_ctrl.InsertColumn(0,"#")
        self.task_list_ctrl.InsertColumn(1, "ETA")
        self.task_list_ctrl.InsertColumn(2, "Task")

        self.task_list_ctrl.SetColumnWidth(0, 40)
        self.task_list_ctrl.SetColumnWidth(1, 40)
        self.task_list_ctrl.SetColumnWidth(2, 400)

        self.add_button = wx.Button(self.task_panel, ADD_TASK_ID, "Add")
        self.complete_button = wx.Button(self.task_panel, COMPLETED_TASK_ID, "Complete")
        self.delete_button = wx.Button(self.task_panel, DELETE_TASK_ID, "Delete")
        
        self.task_vertical_line = wx.StaticLine(self.task_panel, -1, 
                                                        style=wx.LI_VERTICAL)
        
        self.title_label = wx.StaticText(self.task_panel, -1, "Title")
        self.title_text_ctrl = wx.TextCtrl(self.task_panel, -1, "")
        
        self.comments_label = wx.StaticText(self.task_panel, -1, "Comments")
        self.comments_text_ctrl = wx.TextCtrl(self.task_panel, -1, "", 
                                        style=wx.SUNKEN_BORDER|wx.TE_MULTILINE)
        
        self.start_label = wx.StaticText(self.task_panel, -1, "Start date")
        self.startDatePicker = wx.DatePickerCtrl(self.task_panel, size=(160, -1),
                                        style=wx.DP_DROPDOWN|wx.DP_SHOWCENTURY)
        
        self.endDateLabel = wx.StaticText(self.task_panel, -1, "End date")
        self.endDatePicker = wx.DatePickerCtrl(self.task_panel, size=(160,-1),
                                        style=wx.DP_DROPDOWN|wx.DP_SHOWCENTURY)

        #--- Log Panel Controls ---#
        self.log_panel = wx.Panel(self, LOG_PANEL_ID)
        self.AddPage(self.log_panel, "Logs")

        self.log_label = wx.StaticText(self.log_panel, -1, "Log")
        self.log_list_ctrl = wx.ListBox(self.log_panel, LOG_LISTBOX_ID, wx.DefaultPosition,
                                wx.DefaultSize, [], wx.LB_SINGLE|wx.LB_NEEDED_SB, wx.DefaultValidator)
        
        self.log_new_button = wx.Button(self.log_panel, NEW_LOG_ID, "New")
        self.log_delete_button = wx.Button(self.log_panel, DELETE_LOG_ID, "Delete")
        
        self.log_vertical_line = wx.StaticLine(self.log_panel, -1, style=wx.LI_VERTICAL)
        
        self.dateLabel = wx.StaticText(self.log_panel, -1, "Date")
        self.date_text_ctrl = wx.TextCtrl(self.log_panel, -1, size=(180,-1), style=wx.TE_READONLY, 
                                            value="")
        
        self.body_label = wx.StaticText(self.log_panel, -1, "Body")
        self.body_text_ctrl = wx.TextCtrl(self.log_panel, -1, "", 
                                            style=wx.SUNKEN_BORDER|wx.TE_MULTILINE)
        
        #--- Task Overview Controls ---#
##        self.task_overview_panel = wx.Panel(self, -1)
##        self.AddPage(self.task_overview_panel, "Overview of All Tasks")
        
##        self.task_overview_list_ctrl = FocusListCtrl(self.task_overview_panel, -1,
##                                                        style=wx.SUNKEN_BORDER|wx.LC_REPORT)
##        self.task_overview_list_ctrl.InsertColumn(0,"#")
##        self.task_overview_list_ctrl.InsertColumn(1, "ETA")
##        self.task_overview_list_ctrl.InsertColumn(2, "Project")
##        self.task_overview_list_ctrl.InsertColumn(3, "Task")

##        self.task_overview_list_ctrl.SetColumnWidth(0, 40)
##        self.task_overview_list_ctrl.SetColumnWidth(1, 40)
##        self.task_overview_list_ctrl.SetColumnWidth(2, 200)
##        self.task_overview_list_ctrl.SetColumnWidth(3, 400)
        
##        self.task_overview_view_btn = wx.Button(self.task_overview_panel, -1, "View")
##        self.task_overview_add_btn = wx.Button(self.task_overview_panel, -1, "Add")
##        self.task_overview_complete_btn = wx.Button(self.task_overview_panel, -1, "Complete")
##        self.task_overview_delete_btn = wx.Button(self.task_overview_panel, -1, "Delete")
        
        
#        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging)

        self.__do_layout()

    def __do_layout(self):
        """ Layout controls on the Notebook panels. """
        #--- Task Panel ---#
        self.task_root = wx.BoxSizer(wx.HORIZONTAL)
        self.task_left_sizer = wx.BoxSizer(wx.VERTICAL)
        
        self.task_buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.task_buttons_sizer.Add(self.add_button, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
        self.task_buttons_sizer.Add(self.complete_button, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
        self.task_buttons_sizer.Add(self.delete_button, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)

        self.task_left_sizer.Add(self.task_label, 0, wx.ALL|wx.GROW|wx.EXPAND, 5)
        self.task_left_sizer.Add(self.task_list_ctrl, 20, wx.ALL|wx.GROW|wx.EXPAND, 5)
        self.task_left_sizer.Add(self.task_buttons_sizer, 0, wx.ALL|wx.GROW|wx.EXPAND, 5)
        
        self.task_info_sizer = wx.StaticBoxSizer(wx.StaticBox(self.task_panel, -1, "Task Information"),
                                                    wx.VERTICAL)
        
        self.title_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.title_sizer.Add(self.title_label, 0, wx.ALIGN_BOTTOM|wx.TOP|wx.GROW|wx.EXPAND, 20)
        self.title_sizer.Add(self.title_text_ctrl, 1, wx.ALIGN_BOTTOM|wx.ALL|wx.GROW|wx.EXPAND, 15)
        self.task_info_sizer.Add(self.title_sizer, 0, wx.GROW, 0)
        self.task_info_sizer.Add(self.comments_label, 0, wx.ALIGN_LEFT|wx.ALL, 5)
        self.task_info_sizer.Add(self.comments_text_ctrl, 1, wx.GROW|wx.EXPAND|wx.ALIGN_LEFT|wx.ALL, 5)

        self.start_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.start_sizer.Add(self.start_label, 0, wx.ALIGN_TOP|wx.ALIGN_CENTER|wx.RIGHT, 5)
        self.start_sizer.Add(self.startDatePicker, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)

        self.task_info_sizer.Add(self.start_sizer, 0, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.ALL, 5)

        self.end_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.end_sizer.Add(self.endDateLabel, 0, wx.ALIGN_TOP|wx.ALIGN_CENTER|wx.RIGHT, 5)
        self.end_sizer.Add(self.endDatePicker, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)

        self.task_info_sizer.Add(self.end_sizer, 0, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.ALL, 5)
        self.task_root.Add(self.task_left_sizer, 0, wx.GROW|wx.EXPAND|wx.ALIGN_BOTTOM|wx.ALL, 5)
        self.task_root.Add(self.task_vertical_line, 0, 
                            wx.GROW|wx.ALIGN_BOTTOM|wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
        self.task_root.Add(self.task_info_sizer, 1, wx.GROW|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, 5)
        
        self.task_panel.SetAutoLayout(True)
        self.task_panel.SetSizer(self.task_root)

        #--- Log Panel ---#
        self.log_root = wx.BoxSizer(wx.HORIZONTAL)
        self.log_sizer = wx.BoxSizer(wx.VERTICAL)
        self.log_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.log_button_sizer.Add(self.log_new_button, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
        self.log_button_sizer.Add(self.log_delete_button, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)

        self.log_sizer.Add(self.log_label, 0, wx.ALL|wx.GROW|wx.EXPAND, 5)
        self.log_sizer.Add(self.log_list_ctrl, 20, wx.ALL|wx.GROW|wx.EXPAND, 5)
        self.log_sizer.Add(self.log_button_sizer, 0, wx.ALL|wx.GROW|wx.EXPAND, 5)
        self.log_info_sizer = wx.StaticBoxSizer(wx.StaticBox(self.log_panel,
                                                    -1, "Log Information"), wx.VERTICAL)
                                
        self.date_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.date_sizer.Add(self.dateLabel, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER|wx.RIGHT, 15)
        self.date_sizer.Add(self.date_text_ctrl, 0, wx.ALIGN_RIGHT|wx.RIGHT|wx.EXPAND|wx.GROW, 20)
        
        self.log_info_sizer.Add(self.date_sizer, 0, wx.ALIGN_RIGHT|wx.GROW|wx.EXPAND|wx.ALL, 5)
        self.log_info_sizer.Add(self.body_label, 0, wx.ALIGN_LEFT|wx.ALL, 5)
        self.log_info_sizer.Add(self.body_text_ctrl, 1, wx.GROW|wx.EXPAND|wx.ALIGN_LEFT|wx.ALL, 5)

        self.log_root.Add(self.log_sizer, 0, wx.GROW|wx.EXPAND|wx.ALIGN_BOTTOM|wx.ALL, 5)
        self.log_root.Add(self.log_vertical_line, 0, 
                            wx.GROW|wx.ALIGN_BOTTOM|wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
        self.log_root.Add(self.log_info_sizer, 1, wx.GROW|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, 5)

        self.log_panel.SetAutoLayout(True)
        self.log_panel.SetSizer(self.log_root)
        
